﻿// FlmMatrix
// Object representing a matrix
// 
// FLMScan - Featured by FabLab Munich

using System;
using System.Collections.Generic;
using FLMScan.OpenCV;
using log4net;


namespace FLMScan.Geometry
{
    internal class FlmMatrix
    {
        private ILog logger;

        private int rows;
        private int columns;
        private double[,] matrix;        

        public FlmMatrix(int rows, int columns) 
        {
            logger = LogManager.GetLogger(typeof(FlmMatrix));

            this.rows = rows;
            this.columns = columns;
            matrix = new double[rows, columns];
        }
                        
        public double this[int row, int column]
        {
            get { return matrix[row, column]; }
            set { matrix[row, column] = value; }
        }

        public int Rows
        {
            get { return rows; }
        }

        public int Columns
        {
            get { return columns; }
        }

        public CvMat AsCvMat()
        {
            CvMat cvMat;
            cvMat = MatrixWrapperOpenCV.CvCreateMat(rows, columns, OpenCVConstants.CV_64FC1);

            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < columns; j++)
                {
                    MatrixWrapperOpenCV.CvSetReal2D(ref cvMat, i, j, matrix[i, j]);                    
                }
            }

            return cvMat;
        }

        public static FlmMatrix Multiply(FlmMatrix m1, FlmMatrix m2)
        {
            if (m1.columns != m2.rows)
            {
                throw new ArgumentException("Matrix multiplication failed: matrix dimensions not matching!");
            }

            FlmMatrix result = FlmMatrix.Zero(m1.Rows, m2.Columns);
            for (int i = 0; i < result.rows; i++)
            {
                for (int j = 0; j < result.Columns; j++)
                {
                    for (int k = 0; k < m1.Columns; k++)
                    {
                        result[i, j] += m1[i, k] * m2[k, j];
                    }
                }
            }
            return result;
        }

        private static FlmMatrix Add(FlmMatrix m1, FlmMatrix m2)         
        {
            if (m1.Rows != m2.Rows || m1.Columns != m2.Columns)
            {
                throw new ArgumentException("Matrix add failed: matrix dimensions not matching!");
            }

            FlmMatrix r = new FlmMatrix(m1.Rows, m1.Columns);
            for (int i = 0; i < r.rows; i++)
            {
                for (int j = 0; j < r.Columns; j++)
                    r[i, j] = m1[i, j] + m2[i, j];
            }
            return r;
        }

        public static FlmMatrix Zero(int rows, int columns)
        {
            FlmMatrix matrix = new FlmMatrix(rows, columns);
            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < columns; j++)
                {
                    matrix[i, j] = 0;
                }
            }
            
            return matrix;
        }

        public static FlmMatrix FromRawData(int rows, int columns, List<double> data)
        {
            if ( (rows * columns) != data.Count)
            {
                throw new ArgumentException("Matrix creation failed: raw data count not matching!");
            }

            FlmMatrix matrix = new FlmMatrix(rows, columns);
            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < columns; j++)
                {
                    matrix[i, j] = data[i];
                }
            }

            return matrix;
        }

        public static FlmMatrix FromCvMat(CvMat cvMat)
        {
            FlmMatrix matrix = new FlmMatrix(cvMat.rows, cvMat.cols);
            for (int i = 0; i < cvMat.rows; i++)
            {
                for (int j = 0; j < cvMat.cols; j++)
                {
                    matrix[i, j] = MatrixWrapperOpenCV.CvGetReal2D(ref cvMat, i, j);
                }
            }

            return matrix;
        }

        public void Log(String prefix)
        {
            logger.DebugFormat("{0} FlmMatrix [{1},{2}]:", prefix, rows, columns);

            for (int i = 0; i < rows; i++)
            {
                String rowContents = String.Empty;

                for (int j = 0; j < columns; j++)
                {
                    rowContents += "[" + i + "," + j + "]=" + matrix[i, j] + " ";                    
                }

                logger.Debug(rowContents);
            }            
        }
    }
}
